home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / NewsView 1.0.0 / source / TheUNIXPart / NewsDump.c
Encoding:
C/C++ Source or Header  |  1994-03-13  |  25.2 KB  |  911 lines  |  [TEXT/KAHL]

  1. /* News.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    Offline USENET News Dump Generator                                     */
  5. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  6. /*                                                                           */
  7. /*    This software is Public Domain; it may be used for any purpose         */
  8. /*    whatsoever without restriction.                                        */
  9. /*                                                                           */
  10. /*    This package is distributed in the hope that it will be useful,        */
  11. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  12. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  13. /*                                                                           */
  14. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  15. /*                                                                           */
  16. /*****************************************************************************/
  17. /* gcc -g -Wall -ansi -pedantic -o newsdump NewsDump.c */
  18. /* gcc -o newsdump NewsDump.c */
  19.  
  20. /* these settings can be changed */
  21. #define MAXBYTESPERSECOND (2048) /* don't transfer more than this per second */
  22. #define SERVERCMDWAIT (2) /* seconds of delay per NNTP command */
  23. #define NEWSFILENAME ".newslist" /* name of news file */
  24. #define NEWSSERVERNAME "spool.cs.wisc.edu" /* name of news server */
  25. #define SPOOLDIRPATH "/usr/spool/news/" /* path to the news spool directory */
  26. #define USENNTP (1) /* 0 = use /usr/spool/news, 1 = use NNTP server */
  27.  
  28. /* This program is a non-interactive news reader which can use either an */
  29. /* NNTP server or a news spool directory hierarchy to obtain USENET */
  30. /* articles.  A file ".newslist" in the home directory remembers the */
  31. /* number of the last article read for each subscribed group.  Articles */
  32. /* fetched by the program are written to standard output.  The format of */
  33. /* the articles in the stream is as follows: */
  34. /*   - newsgroup name on a line by itself */
  35. /*   - as many lines as needed for the complete article */
  36. /*   - a line containing a single period */
  37. /* If there is a line with a single period inside of the article, then it */
  38. /* is stored as a double period.  Articles are always written in ascending */
  39. /* order of article index, which should also be chronological order. */
  40. /* Usage:  */
  41. /*   - subscribing to a group */
  42. /*       newsdump +<groupname> */
  43. /*     this will not fetch any articles, but only update the .newslist file */
  44. /*     note that no articles from this group will be fetched the next time */
  45. /*     newsdump is run with no arguments.  It must be run another time */
  46. /*     after that in order to get articles from the group.  Also, an added */
  47. /*     group is assumed to be up to date.  Of course, the .newslist file is */
  48. /*     in editable format, so the article index can be adjusted by hand. */
  49. /*   - unsubscribing from a group */
  50. /*       newsdump -<groupname> */
  51. /*     this will not fetch any articles, but only update the .newslist file */
  52. /*   - fetching new articles */
  53. /*       newsdump */
  54. /*     (no arguments.)  this will fetch all available articles, write them */
  55. /*     to standard output, and update the .newslist file. */
  56. /*   - catching up all groups */
  57. /*       newsdump zap */
  58. /*     this will update the article counter for each group to be the most */
  59. /*     recent article, effectively discarding any new news. */
  60.  
  61. #include <stdio.h>
  62. #include <stdlib.h>
  63. #include <string.h>
  64. #include <unistd.h>
  65. #include <limits.h>
  66.  
  67. #if defined(sparc)
  68. #include <varargs.h>
  69. #else
  70. #include <stdarg.h>
  71. #endif
  72.  
  73. #if USENNTP
  74. #include <sys/socket.h>
  75. #include <netinet/in.h>
  76. #include <netdb.h>
  77. #include <arpa/inet.h>
  78. #else
  79. #include <dirent.h>
  80. #endif
  81.  
  82. /* stupidity for SunOS */
  83. #ifndef EXIT_FAILURE
  84. #define EXIT_FAILURE (1)
  85. #endif
  86. #ifndef EXIT_SUCCESS
  87. #define EXIT_SUCCESS (0)
  88. #endif
  89.  
  90. #define True (0 == 0)
  91. #define False (0 != 0)
  92. typedef int Bool;
  93. #define strequ(left,right) (0 == strcmp(left,right))
  94. #define MAXSERVERLINE (4096)
  95. #define MYMAXPATHLEN (1024)
  96.  
  97.  
  98. /* set this to 1 for audit generation or 0 for no debugging */
  99. #define DEBUG (0)
  100.  
  101.  
  102. /******************************************************************************/
  103. /* debugging */
  104.  
  105.  
  106. #if DEBUG
  107. void    AuditPrint(char* First, ...);
  108. #define APRINT(param) AuditPrint param
  109. #define ERROR(cond,cmd) if (cond) cmd
  110. #else
  111. #define APRINT(param)
  112. #define ERROR(cond,cmd)
  113. #endif
  114.  
  115.  
  116. #if DEBUG
  117.  
  118. static FILE*       BugFile = NULL;
  119.  
  120. void          AuditPrint(char* First, ...)
  121.   {
  122.     va_list     ap;
  123.  
  124.     if (BugFile == NULL)
  125.       {
  126.         BugFile = fopen("audit","w");
  127.         if (BugFile == NULL)
  128.           {
  129.             perror("Couldn't open 'audit' debug file for writing");
  130.             exit(EXIT_FAILURE);
  131.           }
  132.       }
  133.     va_start(ap,First);
  134.     vfprintf(BugFile,First,ap);
  135.     va_end(ap);
  136.     putc('\n',BugFile);
  137.     fflush(BugFile);
  138.   }
  139. #endif
  140.  
  141.  
  142. /******************************************************************************/
  143. /* group list stuff */
  144.  
  145.  
  146. typedef struct GroupRec
  147.   {
  148.     char*             GroupName;
  149.     long              LastReadArticle;
  150.     struct GroupRec*  Next;
  151.   } GroupRec;
  152.  
  153.  
  154. typedef struct GroupListRec
  155.   {
  156.     GroupRec*     List;
  157.   } GroupListRec;
  158.  
  159.  
  160. GroupListRec*     ReadNewsList(void);
  161. void              Subscribe(GroupListRec* List, char* Name);
  162. void              Unsubscribe(GroupListRec* List, char* Name);
  163. void              WriteAndDisposeNewsList(GroupListRec* List);
  164. long              GetGroupLastReadArticle(GroupListRec* List, char* Name);
  165. void              SetGroupLastReadArticle(GroupListRec* List, char* Name,
  166.                     long LastRead);
  167. long              GroupListLength(GroupListRec* List);
  168. char*             GetGroupName(GroupListRec* List, long Index);
  169.  
  170.  
  171. /* read the .newslist file in */
  172. GroupListRec*     ReadNewsList(void)
  173.   {
  174.     GroupListRec*   List;
  175.     FILE*           ReadFile;
  176.     char            Path[MYMAXPATHLEN];
  177.     char*           Home;
  178.  
  179.     APRINT(("+ReadNewsList"));
  180.     List = malloc(sizeof(GroupListRec));
  181.     List->List = NULL;
  182.     Home = getenv("HOME");
  183.     if (Home == NULL)
  184.       {
  185.         Home = "";
  186.       }
  187.     if (strlen(Home) + strlen(NEWSFILENAME) + 1 > MYMAXPATHLEN - 1)
  188.       {
  189.         fprintf(stderr,"path to "NEWSFILENAME" is too long\n");
  190.         exit(EXIT_FAILURE);
  191.       }
  192.     strcpy(Path,Home);
  193.     strcat(Path,"/");
  194.     strcat(Path,NEWSFILENAME);
  195.     ReadFile = fopen(Path,"rb");
  196.     if (ReadFile != NULL)
  197.       {
  198.         GroupRec*     Tail;
  199.  
  200.         Tail = NULL;
  201.         while (!feof(ReadFile))
  202.           {
  203.             int           NameLen;
  204.             char*         Name;
  205.             int           Value;
  206.             GroupRec*     New;
  207.             Bool          Negative;
  208.  
  209.             Name = malloc(0);
  210.             NameLen = 0;
  211.             while ((Value = getc(ReadFile)),(Value != EOF) && (Value != ':'))
  212.               {
  213.                 Name = realloc(Name,NameLen + 1);
  214.                 Name[NameLen] = Value;
  215.                 NameLen += 1;
  216.               }
  217.             if ((Value == EOF))
  218.               {
  219.                 goto EndOfFileSkipTownNow;
  220.               }
  221.             Name = realloc(Name,NameLen + 1);
  222.             Name[NameLen] = 0;
  223.             fprintf(stderr,"Newsgroup '%s' info loaded.\n",Name);
  224.             New = malloc(sizeof(GroupRec));
  225.             New->GroupName = Name;
  226.             New->LastReadArticle = 0;
  227.             Negative = False;
  228.             while ((Value = getc(ReadFile)),(Value != EOF) && (Value != '\n'))
  229.               {
  230.                 if (Value == '-')
  231.                   {
  232.                     Negative = True;
  233.                   }
  234.                  else
  235.                   {
  236.                     New->LastReadArticle = (10 * New->LastReadArticle)
  237.                       + (Value - '0');
  238.                   }
  239.               }
  240.             if (Negative)
  241.               {
  242.                 New->LastReadArticle = - New->LastReadArticle;
  243.               }
  244.             /* link the data structure */
  245.             New->Next = NULL;
  246.             if (Tail == NULL)
  247.               {
  248.                 List->List = New;
  249.               }
  250.              else
  251.               {
  252.                 Tail->Next = New;
  253.               }
  254.             Tail = New;
  255.           }
  256.        EndOfFileSkipTownNow:
  257.         fclose(ReadFile);
  258.       }
  259.     APRINT(("-ReadNewsList %p",List));
  260.     return List;
  261.   }
  262.  
  263.  
  264. /* add a new group to the news list */
  265. void              Subscribe(GroupListRec* List, char* Name)
  266.   {
  267.     GroupRec*       Scan;
  268.     GroupRec*       Lag;
  269.  
  270.     APRINT(("+Subscribe %p %s",List,Name));
  271.     Scan = List->List;
  272.     Lag = NULL;
  273.     while ((Scan != NULL) && !strequ(Name,Scan->GroupName))
  274.       {
  275.         Lag = Scan;
  276.         Scan = Scan->Next;
  277.       }
  278.     if (Scan == NULL)
  279.       {
  280.         GroupRec*       New;
  281.  
  282.         /* if Scan == NULL then we got to the end of the list without */
  283.         /* finding the group, so we can append it */
  284.         New = malloc(sizeof(GroupRec));
  285.         New->GroupName = malloc(strlen(Name) + 1);
  286.         strcpy(New->GroupName,Name);
  287.         New->LastReadArticle = -1;
  288.         New->Next = NULL;
  289.         if (Lag != NULL)
  290.           {
  291.             Lag->Next = New;
  292.           }
  293.          else
  294.           {
  295.             List->List = New;
  296.           }
  297.       }
  298.     APRINT(("-Subscribe"));
  299.   }
  300.  
  301.  
  302. /* remove a group from the list */
  303. void              Unsubscribe(GroupListRec* List, char* Name)
  304.   {
  305.     GroupRec*       Scan;
  306.     GroupRec*       Lag;
  307.  
  308.     APRINT(("+Unsubscribe %p %s",List,Name));
  309.     Scan = List->List;
  310.     Lag = NULL;
  311.     while ((Scan != NULL) && !strequ(Name,Scan->GroupName))
  312.       {
  313.         Lag = Scan;
  314.         Scan = Scan->Next;
  315.       }
  316.     if (Scan != NULL)
  317.       {
  318.         if (Lag != NULL)
  319.           {
  320.             Lag->Next = Scan->Next;
  321.           }
  322.          else
  323.           {
  324.             List->List = Scan->Next;
  325.           }
  326.         free(Scan->GroupName);
  327.         free(Scan);
  328.       }
  329.     APRINT(("-Unsubscribe"));
  330.   }
  331.  
  332.  
  333. /* write the newslist back to the .newslist file */
  334. void              WriteAndDisposeNewsList(GroupListRec* List)
  335.   {
  336.     FILE*           Out;
  337.     GroupRec*       Scan;
  338.     char            Path[MYMAXPATHLEN];
  339.     char*           Home;
  340.  
  341.     APRINT(("+WriteAndDisposeNewsList %p",List));
  342.     Home = getenv("HOME");
  343.     if (Home == NULL)
  344.       {
  345.         Home = "";
  346.       }
  347.     if (strlen(Home) + strlen(NEWSFILENAME) + 1 > MYMAXPATHLEN - 1)
  348.       {
  349.         fprintf(stderr,"path to "NEWSFILENAME" is too long\n");
  350.         exit(EXIT_FAILURE);
  351.       }
  352.     strcpy(Path,Home);
  353.     strcat(Path,"/");
  354.     strcat(Path,NEWSFILENAME);
  355.     Out = fopen(Path,"wb");
  356.     if (Out != NULL)
  357.       {
  358.         Scan = List->List;
  359.         while (Scan != NULL)
  360.           {
  361.             fprintf(Out,"%s:%ld\n",Scan->GroupName,Scan->LastReadArticle);
  362.             fprintf(stderr,"Writing newsgroup '%s' info.\n",Scan->GroupName);
  363.             Scan = Scan->Next;
  364.           }
  365.         fclose(Out);
  366.       }
  367.      else
  368.       {
  369.         fprintf(stderr,"Couldn't write updated "NEWSFILENAME" file.\n");
  370.       }
  371.     Scan = List->List;
  372.     while (Scan != NULL)
  373.       {
  374.         GroupRec*     Temp;
  375.  
  376.         Temp = Scan;
  377.         Scan = Scan->Next;
  378.         free(Temp->GroupName);
  379.         free(Temp);
  380.       }
  381.     free(List);
  382.     APRINT(("-WriteAndDisposeNewsList"));
  383.   }
  384.  
  385.  
  386. long              GetGroupLastReadArticle(GroupListRec* List, char* Name)
  387.   {
  388.     GroupRec*       Scan;
  389.  
  390.     APRINT(("+GetGroupLastReadArticle %p %s",List,Name));
  391.     Scan = List->List;
  392.     while ((Scan != NULL) && !strequ(Name,Scan->GroupName))
  393.       {
  394.         Scan = Scan->Next;
  395.       }
  396.     if (Scan != NULL)
  397.       {
  398.         APRINT(("-GetGroupLastReadArticle %ld",Scan->LastReadArticle));
  399.         return Scan->LastReadArticle;
  400.       }
  401.     fprintf(stderr,"Internal error:  GetGroupLastReadArticle unknown group.\n");
  402.     exit(EXIT_FAILURE);
  403.   }
  404.  
  405. void              SetGroupLastReadArticle(GroupListRec* List, char* Name,
  406.                     long LastRead)
  407.   {
  408.     GroupRec*       Scan;
  409.  
  410.     APRINT(("+SetGroupLastReadArticle %p %s %ld",List,Name,LastRead));
  411.     Scan = List->List;
  412.     while ((Scan != NULL) && !strequ(Name,Scan->GroupName))
  413.       {
  414.         Scan = Scan->Next;
  415.       }
  416.     if (Scan != NULL)
  417.       {
  418.         Scan->LastReadArticle = LastRead;
  419.       }
  420.      else
  421.       {
  422.         fprintf(stderr,
  423.           "Internal error:  SetGroupLastReadArticle unknown group.\n");
  424.         exit(EXIT_FAILURE);
  425.       }
  426.     APRINT(("-SetGroupLastReadArticle"));
  427.   }
  428.  
  429.  
  430. long              GroupListLength(GroupListRec* List)
  431.   {
  432.     long            Count;
  433.     GroupRec*       Scan;
  434.  
  435.     Count = 0;
  436.     Scan = List->List;
  437.     while (Scan != NULL)
  438.       {
  439.         Count += 1;
  440.         Scan = Scan->Next;
  441.       }
  442.     return Count;
  443.   }
  444.  
  445.  
  446. char*             GetGroupName(GroupListRec* List, long Index)
  447.   {
  448.     GroupRec*       Scan;
  449.  
  450.     Scan = List->List;
  451.     while (Index > 0)
  452.       {
  453.         Index -= 1;
  454.         Scan = Scan->Next;
  455.       }
  456.     return Scan->GroupName;
  457.   }
  458.  
  459.  
  460. /******************************************************************************/
  461. /* stuff for getting articles from wherever they should be gotten from */
  462.  
  463.  
  464. void              OpenServer(void);
  465. void              CloseServer(void);
  466. Bool              GetServerGroupInfo(char* GroupName, long* Lowbound,
  467.                     long* Highbound);
  468. Bool              ServerReadArticle(char* GroupName, long ArticleIndex);
  469.  
  470.  
  471. #if USENNTP /* { */
  472. /* TCP stream stuff */
  473.  
  474. /* these two routines are only needed for TCP stuff */
  475. void              ServerWrite(char* String);
  476. void              ServerRead(char Buffer[MAXSERVERLINE]);
  477.  
  478. /* these file descriptors are fdopened on the socket */
  479. static FILE*      NNTPIn;
  480. static FILE*      NNTPOut;
  481.  
  482.  
  483. void              OpenServer(void)
  484.   {
  485.     int             TheSocket;
  486.     int             TheOtherSocket;
  487.     struct sockaddr_in Where;
  488.     char            MyStupidBuffer[MAXSERVERLINE];
  489.     struct hostent* HostEnt;
  490.  
  491.     APRINT(("+OpenServer"));
  492.     /* obtain a TCP socket */
  493.     TheSocket = socket(PF_INET,SOCK_STREAM,0);
  494.     if (TheSocket < 0)
  495.       {
  496.         perror("OpenServer:  Can't open socket");
  497.         exit(EXIT_FAILURE);
  498.       }
  499.     /* establish a connection on the socket */
  500.     HostEnt = gethostbyname(NEWSSERVERNAME);
  501.     if (HostEnt == NULL)
  502.       {
  503.         perror("OpenServer:  NULL response from gethostbyname");
  504.         exit(EXIT_FAILURE);
  505.       }
  506.     if (HostEnt->h_length < 1)
  507.       {
  508.         fprintf(stderr,"OpenServer:  gethostbyname gave zero addresses");
  509.         exit(EXIT_FAILURE);
  510.       }
  511.     memset((char *)&Where,0,sizeof(Where));
  512.     Where.sin_family = AF_INET;
  513.     Where.sin_port = htons(119);
  514.     memcpy((char *)&Where.sin_addr,HostEnt->h_addr,HostEnt->h_length);
  515.     if (0 > connect(TheSocket,(void*)&Where,sizeof(Where)))
  516.       {
  517.         perror("OpenServer:  Can't connect to "NEWSSERVERNAME);
  518.         exit(EXIT_FAILURE);
  519.       }
  520.     /* duplicate the socket so that fclose works properly */
  521.     TheOtherSocket = dup(TheSocket);
  522.     if (TheOtherSocket < 0)
  523.       {
  524.         perror("OpenServer:  Can't duplicate the socket");
  525.         exit(EXIT_FAILURE);
  526.       }
  527.     /* make the input and output streams */
  528.     NNTPIn = fdopen(TheSocket,"r");
  529.     NNTPOut = fdopen(TheSocket,"w");
  530.     if ((NNTPIn == NULL) || (NNTPOut == NULL))
  531.       {
  532.         perror("OpenServer:  Can't fdopen socket");
  533.       }
  534.     /* turn off buffering on the output stream */
  535.     setbuf(NNTPOut,NULL);
  536.     /* read in the welcome crud from the server */
  537.     do
  538.       {
  539.         /* we wait for the message 200 or 201 from the server */
  540.         ServerRead(MyStupidBuffer);
  541.       } while (MyStupidBuffer[0] != '2');
  542.     APRINT(("-OpenServer"));
  543.   }
  544.  
  545.  
  546. void              CloseServer(void)
  547.   {
  548.     /* close socket */
  549.     APRINT(("+CloseServer"));
  550.     ServerWrite("QUIT");
  551.     fclose(NNTPOut);
  552.     fclose(NNTPIn);
  553.     APRINT(("-CloseServer"));
  554.   }
  555.  
  556.  
  557. void              ServerWrite(char* String)
  558.   {
  559.     APRINT(("+ServerWrite '%s'",String));
  560.     sleep(SERVERCMDWAIT);
  561.     fprintf(NNTPOut,"%s\n",String);
  562.     APRINT(("-ServerWrite"));
  563.   }
  564.  
  565.  
  566. void              ServerRead(char Buffer[MAXSERVERLINE])
  567.   {
  568.     int             Character;
  569.     int             Index;
  570.     static long     SleepByteCounter = 0;
  571.  
  572.     APRINT(("+ServerRead"));
  573.     Index = 0;
  574.    LoopPoint:
  575.     Character = getc(NNTPIn);
  576.     SleepByteCounter += 1;
  577.     if (SleepByteCounter > MAXBYTESPERSECOND)
  578.       {
  579.         /* sleep for 1 second & reset counter */
  580.         /* this reduces the load on the news server */
  581.         SleepByteCounter = 0;
  582.         sleep(1);
  583.       }
  584.     if (Character == '\n')
  585.       {
  586.         while ((Index > 0) && ((Buffer[Index - 1] == '\n')
  587.           || (Buffer[Index - 1] == '\r')))
  588.           {
  589.             Index -= 1;
  590.           }
  591.         Buffer[Index] = 0;
  592.         APRINT(("-ServerRead '%s'",Buffer));
  593.         return;
  594.       }
  595.     if (Index < MAXSERVERLINE - 1)
  596.       {
  597.         Buffer[Index] = Character;
  598.         Index += 1;
  599.       }
  600.     goto LoopPoint;
  601.   }
  602.  
  603.  
  604. Bool              GetServerGroupInfo(char* GroupName, long* Lowbound,
  605.                     long* Highbound)
  606.   {
  607.     char            Buffer[MAXSERVERLINE];
  608.     long            ReturnCode;
  609.     long            NumArticles;
  610.  
  611.     APRINT(("+GetServerGroupInfo %s",GroupName));
  612.     sprintf(Buffer,"GROUP %s",GroupName);
  613.     ServerWrite(Buffer);
  614.     ServerRead(Buffer);
  615.     if (Buffer[0] == '2')
  616.       {
  617.         if (4 != sscanf(Buffer,"%ld %ld %ld %ld",&ReturnCode,&NumArticles,
  618.           /*NB*/Lowbound,/*NB*/Highbound))
  619.           {
  620.             APRINT(("-GetServerGroupInfo False"));
  621.             return False;
  622.           }
  623.         APRINT(("-GetServerGroupInfo %ld..%ld",*Lowbound,*Highbound));
  624.         return True;
  625.       }
  626.     APRINT(("-GetServerGroupInfo False"));
  627.     return False;
  628.   }
  629.  
  630.  
  631. /* read an article from the server.  it assumes that the group has been */
  632. /* selected on the server. */
  633. Bool              ServerReadArticle(char* GroupName, long ArticleIndex)
  634.   {
  635.     char            Buffer[MAXSERVERLINE];
  636.  
  637.     APRINT(("+ServerReadArticle %s %ld",GroupName,ArticleIndex));
  638.     sprintf(Buffer,"ARTICLE %ld",(long)ArticleIndex);
  639.     ServerWrite(Buffer);
  640.     ServerRead(Buffer);
  641.     if (0 == memcmp(Buffer,"400",3))
  642.       {
  643.         fprintf(stderr,"Unexpected server termination.\n");
  644.         exit(EXIT_FAILURE);
  645.       }
  646.     else if (Buffer[0] == '4')
  647.       {
  648.         APRINT(("-ServerReadArticle False"));
  649.         return False; /* article nonexistent */
  650.       }
  651.     /* else article follows */
  652.     printf("%s\n",GroupName);
  653.     do
  654.       {
  655.         ServerRead(Buffer);
  656.         printf("%s\n",Buffer);
  657.       } while (!strequ(Buffer,"."));
  658.     APRINT(("-ServerReadArticle True"));
  659.     return True;
  660.   }
  661.  
  662.  
  663. #else /* } { */
  664.  
  665.  
  666. void              OpenServer(void)
  667.   {
  668.     /* no action taken when using spool directory */
  669.   }
  670.  
  671.  
  672. void              CloseServer(void)
  673.   {
  674.     /* no action taken */
  675.   }
  676.  
  677.  
  678. /* make a newsgroup name into a path name. */
  679. static void       MakeNewsgroupPath(char FilenameBuf[MYMAXPATHLEN],
  680.                     char* NewsgroupName)
  681.   {
  682.     int             Index;
  683.     int             NameScan;
  684.  
  685.     strcpy(FilenameBuf,SPOOLDIRPATH);
  686.     Index = strlen(FilenameBuf);
  687.     NameScan = 0;
  688.     while ((NameScan < MYMAXPATHLEN - 1) && (NewsgroupName[NameScan] != 0))
  689.       {
  690.         if (NewsgroupName[NameScan] == '.')
  691.           {
  692.             FilenameBuf[Index] = '/';
  693.           }
  694.          else
  695.           {
  696.             FilenameBuf[Index] = NewsgroupName[NameScan];
  697.           }
  698.         NameScan += 1;
  699.         Index += 1;
  700.       }
  701.     FilenameBuf[Index] = 0;
  702.   }
  703.  
  704.  
  705. #define DIRTYPE dirent
  706. Bool              GetServerGroupInfo(char* GroupName, long* Lowbound,
  707.                     long* Highbound)
  708.   {
  709.     DIR*            Directory;
  710.     struct DIRTYPE* DirEntry;
  711.     char            PathName[MYMAXPATHLEN];
  712.  
  713.     MakeNewsgroupPath(PathName,GroupName);
  714.     *Lowbound = LONG_MAX;
  715.     *Highbound = 0;
  716.     Directory = opendir(PathName);
  717.     if (Directory == NULL)
  718.       {
  719.         return False;
  720.       }
  721.     while ((DirEntry = readdir(Directory)) != NULL)
  722.       {
  723.         long            Candidate;
  724.  
  725.         Candidate = atol(DirEntry->d_name);
  726.         if ((Candidate < *Lowbound) || (Candidate > *Highbound))
  727.           {
  728.             char*           Scan;
  729.  
  730.             for (Scan = DirEntry->d_name; *Scan != 0; Scan += 1)
  731.               {
  732.                 if ((*Scan < '0') || (*Scan > '9'))
  733.                   {
  734.                     goto NotAnArticle;
  735.                   }
  736.               }
  737.             if (Candidate < *Lowbound)
  738.               {
  739.                 *Lowbound = Candidate;
  740.               }
  741.             if (Candidate > *Highbound)
  742.               {
  743.                 *Highbound = Candidate;
  744.               }
  745.           }
  746.        NotAnArticle:
  747.         ;
  748.       }
  749.     closedir(Directory);
  750.     if (*Lowbound == LONG_MAX)
  751.       {
  752.         *Lowbound = 0;
  753.       }
  754.     return True;
  755.   }
  756.  
  757.  
  758. Bool              ServerReadArticle(char* GroupName, long ArticleIndex)
  759.   {
  760.     char          PathName[MYMAXPATHLEN];
  761.     char          ArtName[64];
  762.     FILE*         DaFile;
  763.  
  764.     MakeNewsgroupPath(PathName,GroupName);
  765.     sprintf(ArtName,"/%ld",ArticleIndex);
  766.     if (strlen(PathName) + strlen(ArtName) > MYMAXPATHLEN - 1)
  767.       {
  768.         return False; /* string too long */
  769.       }
  770.     strcat(PathName,ArtName);
  771.     DaFile = fopen(PathName,"r");
  772.     if (DaFile == NULL)
  773.       {
  774.         return False;
  775.       }
  776.     printf("%s\n",GroupName);
  777.     while (!feof(DaFile))
  778.       {
  779.         char          Buffer[MAXSERVERLINE];
  780.  
  781.         fgets(Buffer,MAXSERVERLINE,DaFile);
  782.         if ((strlen(Buffer) > 0) && (Buffer[strlen(Buffer) - 1] == '\n'))
  783.           {
  784.             Buffer[strlen(Buffer) - 1] = 0;
  785.           }
  786.         if (strequ(Buffer,"."))
  787.           {
  788.             puts("..");
  789.           }
  790.          else
  791.           {
  792.             puts(Buffer);
  793.           }
  794.       }
  795.     fclose(DaFile);
  796.     puts("."); /* finally, mark end of thang */
  797.     return True;
  798.   }
  799.  
  800.  
  801. #endif /* } */
  802.  
  803.  
  804. /******************************************************************************/
  805. /* main thing */
  806.  
  807.  
  808. int               main(int argc, char** argv)
  809.   {
  810.     GroupListRec*   GroupList;
  811.  
  812.     APRINT(("+main"));
  813.     GroupList = ReadNewsList();
  814.     if (GroupList != NULL)
  815.       {
  816.         if ((argc == 1) || ((argc == 2) && (strequ("zap",argv[1]))))
  817.           {
  818.             long              GroupScan;
  819.             int               DontReadGroups;
  820.  
  821.             DontReadGroups = ((argc == 2) && (strequ("zap",argv[1])));
  822.             OpenServer();
  823.             for (GroupScan = 0; GroupScan < GroupListLength(GroupList);
  824.               GroupScan += 1)
  825.               {
  826.                 char*             GroupName;
  827.                 int               SuccessFlag;
  828.                 long              Lowbound;
  829.                 long              Highbound;
  830.  
  831.                 GroupName = GetGroupName(GroupList,GroupScan);
  832.                 fprintf(stderr,"Group %s\n",GroupName);
  833.                 SuccessFlag = GetServerGroupInfo(GroupName,&Lowbound,&Highbound);
  834.                 if (DontReadGroups)
  835.                   {
  836.                     long              OldLastRead;
  837.  
  838.                     fprintf(stderr,"Zapping group %s\n",GroupName);
  839.                     SetGroupLastReadArticle(GroupList,GroupName,Highbound);
  840.                   }
  841.                 else if (!SuccessFlag)
  842.                   {
  843.                     fprintf(stderr,"Can't get group %s\n",GroupName);
  844.                   }
  845.                 else
  846.                   {
  847.                     long              OldLastRead;
  848.                     long              ArtScan;
  849.  
  850.                     OldLastRead = GetGroupLastReadArticle(GroupList,GroupName);
  851.                     if (OldLastRead < 0)
  852.                       {
  853.                         /* new group, don't want to haul all articles over */
  854.                         OldLastRead = Highbound;
  855.                       }
  856.                     if (OldLastRead < Lowbound - 1)
  857.                       {
  858.                         OldLastRead = Lowbound - 1;
  859.                       }
  860.                     if (OldLastRead > Highbound)
  861.                       {
  862.                         /* has newsgroup been reset maybe? */
  863.                         OldLastRead = Lowbound - 1;
  864.                       }
  865.                     for (ArtScan = OldLastRead + 1; ArtScan <= Highbound;
  866.                       ArtScan += 1)
  867.                       {
  868.                         if (ServerReadArticle(GroupName,ArtScan))
  869.                           {
  870.                             /* we successfully read the article */
  871.                             fprintf(stderr,"Got %s: %ld\n",GroupName,ArtScan);
  872.                           }
  873.                          else
  874.                           {
  875.                             /* article wasn't available */
  876.                             fprintf(stderr,"Unavail %s: %ld\n",GroupName,
  877.                               ArtScan);
  878.                           }
  879.                       }
  880.                     SetGroupLastReadArticle(GroupList,GroupName,Highbound);
  881.                   }
  882.               }
  883.             CloseServer();
  884.           }
  885.          else
  886.           {
  887.             int           Scan;
  888.  
  889.             for (Scan = 1; Scan < argc; Scan += 1)
  890.               {
  891.                 if (argv[Scan][0] == '+')
  892.                   {
  893.                     Subscribe(GroupList,&(argv[Scan][1]));
  894.                   }
  895.                 else if (argv[Scan][0] == '-')
  896.                   {
  897.                     Unsubscribe(GroupList,&(argv[Scan][1]));
  898.                   }
  899.                 else
  900.                   {
  901.                     printf("Misformed argument '%s'\n",argv[Scan]);
  902.                     exit(EXIT_FAILURE);
  903.                   }
  904.               }
  905.           }
  906.         WriteAndDisposeNewsList(GroupList);
  907.       }
  908.     APRINT(("-main"));
  909.     exit(EXIT_SUCCESS);
  910.   }
  911.